use std::fs::remove_file;
use std::path::PathBuf;
use std::time::Duration;

use wtask_base::export::{log_debug, log_error, log_info, select, tokio_sleep, tokio_spawn};
use wtask_base::{
    app::get_wtask_root_dir,
    cmd::create_file,
    error::WResult,
    machine::WSpawn,
    tcp::tcp_listen
};
use wtask_file::{Builder, HyperError, IncomingBody, MachineWebDAVServer, Method, Request, TokioIo, WebDAVServer, service_fn};

use crate::basic::{internal_error, not_found};
use crate::handle::app::response_app;
use crate::handle::flow::response_flow;
use crate::handle::room::response_room;
use crate::handle::login::response_login;
use crate::handle::logout::response_logout;
use crate::handle::port::response_port;
use crate::html::response_html;



pub fn get_file_web_running() -> PathBuf {
    get_wtask_root_dir().join("wtask.web.running")
}

pub async fn web(port: u32, wsport: u32, webwebdav: u32, machinewebdav: u32) -> WResult<()> {
    let listener = tcp_listen(port, "Web")?;
    let listener_webdav = tcp_listen(webwebdav, "WEBWebDav")?;
    let mut wspawn = WSpawn::default();
    let (_t, mut kill_rx) = wspawn.catch_pause_signal(async move {
        let _ = remove_file(get_file_web_running());
    });
    let dav_server = WebDAVServer::default();
    
    tokio_spawn(async move {
        tokio_sleep(Duration::from_secs(5)).await;
        let p = get_file_web_running();
        create_file(&p, &format!("{port}")).unwrap();
    });

    let kill_rx_clone = kill_rx.clone();
    tokio_spawn(async move {
        let webdav = MachineWebDAVServer::new(machinewebdav);
        let v = webdav.listen(kill_rx_clone).await;
        if let Err(e) = v {
            log_error!("MachineWebDav stop: {e}");
        }
    });

    loop {
        select! {
            Ok((stream, _)) = listener.accept() => {
                log_debug!("Server get a connect ...");
                let io = TokioIo::new(stream);
                wspawn.spawn(async move {
                    let service = service_fn(|req: Request<IncomingBody>| {
                        async move {
                            let uri = req.uri().path();
                            let token = req.headers().get("Token").cloned();
                            log_debug!("Get: {:?} {}", req.method(), uri);
                            let res = match (req.method(), token, uri) {
                                (&Method::GET, _, _) => response_html(uri),
                                (&Method::POST, Some(token), "/api/app") => response_app(req, token.to_str().unwrap()).await,
                                (&Method::POST, Some(token), "/api/login") => response_login(req, token.to_str().unwrap()).await,
                                (&Method::POST, Some(token), "/api/logout") => response_logout(req, token.to_str().unwrap()).await,
                                (&Method::POST, Some(token), "/api/room") => response_room(req, token.to_str().unwrap(), wsport).await,
                                (&Method::POST, Some(token), "/api/port") => response_port(req, token.to_str().unwrap()).await,
                                (&Method::POST, Some(token), "/api/flow") => response_flow(req, token.to_str().unwrap()).await,
                                _ => Ok(not_found())
                            };
                            match res {
                                Ok(res) => Ok::<_, HyperError>(res),
                                Err(e) => {
                                    log_error!("web error: {e}");
                                    Ok(internal_error())
                                }
                            }
                        }
                    });
                    if let Err(err) = Builder::new().serve_connection(io, service).await {
                        log_error!("Failed to serve connection: {err:?}");
                    }
                    // session_now2.write().await.1.clean(expire);
                });
            },
            Ok((stream, _)) = listener_webdav.accept() => {
                let dav_server = dav_server.clone();
                let io = TokioIo::new(stream);
                tokio_spawn(async move {
                    let service = service_fn(|req: Request<IncomingBody>| {
                        let dav_server = dav_server.clone();
                        async move { dav_server.handle(req).await }
                    });
                    if let Err(e) = Builder::new().serve_connection(io, service).await {
                        log_error!("webwebdav error: {e}");
                    }
                });
            },
            _ = kill_rx.changed() => {
                log_info!("Shutting down web listener ...");
                break;
            }
        }
    }
    Ok(())
}

